// =====================================================================================
// 
//       Filename:  attributs.h
// 
//    Description:  Description des attributs d'un élément cartographique
// 
//        Version:  1.0
//        Created:  21/07/2009 23:33:53
//       Revision:  none
//       Compiler:  g++
// 
//         Author:  François Hissel (fh), francois.hissel@m4x.org
//        Company:  
// 
// =====================================================================================


#ifndef  ATTRIBUTS_H_INC
#define  ATTRIBUTS_H_INC

#include	<string>
#include	<iostream>
#include	<vector>
#include	<exception>

using namespace std;

/**
 * \class DataException
 * \brief Exception soulevée lorsqu'une erreur survient pendant une opération sur les tables de données, en utilisant les classes Champ, DescriptionTable ou Enregistrement
 *
 * La classe permet de gérer les erreurs qui surviennent pendant les opérations sur les tables de données.
 */
class DataException:public exception {
	public:
		/**
		 * \brief Ensemble des codes d'erreur possibles
		 *
		 * L'énumération rassemble les codes d'erreur qui peuvent être retournés lorsqu'une opération sur des tables d'attributs a généré une erreur.
		 */
		enum Code {
			BAD_DESCRIPTION,	//!< Erreur lors de la définition des champs de la table
			BAD_REFERENCE,	//!< Tentative d'accès à un champ invalide
			BAD_FORMAT	//!< Format de données incorrect
			} code;	//!< Code de l'erreur
		string message;	//!< Message descriptif de l'erreur
		DataException(Code pcode):code(pcode) {}	//!< Constructeur sans définition du message
		DataException(Code pcode,string pmessage):code(pcode),message(pmessage) {}	//!< Constructeur avec définition du message
		~DataException() throw() {}	//!< Destructeur standard
		const char* what() const throw();	//!< Retourne un message de description de l'erreur
};

/**
 * \struct Champ
 * \brief Définition d'un champ d'une table de base de données
 *
 * La classe contient la définition d'un champ d'une table de base de données. Le champ est décrit par son type et par son nom. Un enregistrement est ainsi composé de plusieurs champs pour chacun desquels une valeur est associée.
 */
struct Champ {
	/**
	 * \enum A_Type
	 * \brief Constantes représentant le type de données que peut prendre un attribut
	 *
	 * L'énumération contient des constantes utilisées pour représenter le type de données stockées par l'attribut.
	 */
	typedef enum {
		NUL = 0,
		ENTIER = 1,	//!< Type entier
		ENTIER_LONG = 2,	//!< Type entier long
		FLOTTANT = 3,	//!< Type décimal, représenté par un flottant
		DOUBLE = 4,	//!< Type décimal, représenté par un double
		CHAINE = 5	//!< Type chaîne de caractères, représenté par un objet de type string
	} A_Type;

	A_Type type;	//!< Type des valeurs contenues par le champ
	string nom;	//!< Nom du champ
	Champ():type(NUL),nom("") {}	//!< Constructeur standard sans initialisation de données
	Champ(A_Type ptype,string pnom):type(ptype),nom(pnom) {}	//!< Constructeur standard avec initialisation des membres
	int taille() const;	//!< Taille en octets du champ
};

/**
 * \class DescriptionTable
 * \brief Définition des types de champs d'une table de données.
 *
 * La classe contient la description des champs d'une table de base de données, adaptée aux formats courants des fichiers MID. Pour chacune des bases de données traitées, cette classe ne doit être stockée qu'une seule fois en mémoire. Chaque enregistrement y fait référence et utilise la description des types pour stocker et analyser ses propres données.
 */
class DescriptionTable {
	public:
		int nombre() const {return _nombre;}	//!< Nombre de champs de la table
		int taille() const {return _taille;}	//!< Taille totale en octets d'un enregistrement

		/**
		 * \brief Retourne l'offset en octets à appliquer à la structure data pour obtenir la position de l'élément spécifié
		 *
		 * La méthode retourne la position de l'élément spécifié par le paramètre index dans la structure data, en indiquant le nombre d'octets entre le début de la structure et le début de l'élément.
		 * \param index Numéro de l'élément dont on souhaite obtenir la position
		 * \return Offset en octets de l'élément spécifié par rapport au début de la structure
		 */
		int position(int index) const {return positions[index];}

		/**
		 * \brief Retourne le champ situé à la position index
		 *
		 * La méthode retourne le champ situé à la position index dans la structure de données data.
		 * \param index Numéro de l'élément dont le champ doit être déterminé
		 * \return Descriptif du champ de données à la position spécifiée
		 */
		Champ champ(int index) const {return champs[index];}

		/**
		 * \brief Constructeur de description de table, avec mention du nombre d'éléments
		 *
		 * Constructeur de description de table, qui précise le nombre de champs envisagés. Ce nombre ne pourra pas être modifié par la suite. Il est donc indispensable d'en donner la valeur exacte dès son allocation. Cependant, cette valeur est normalement disponible dès les premières lignes des fichiers MIF.
		 */
		DescriptionTable(int pnombre);
		~DescriptionTable();	//!< Destructeur standard, libère les ressources occupées par la description

		/**
		 * \brief Ajoute un champ à la description de la table de données
		 *
		 * La méthode permet d'ajouter un champ à la table de données. Elle met à jour la taille des enregistrements nécessaires et le tableau positions qui indique où rechercher les données dans l'espace de stockage
		 * \param c Champ à ajouter
		 */
		void ajoute_champ(const Champ &c);

		/**
		 * \brief Retourne l'index du champ dont le nom est indiqué
		 *
		 * La méthode recherche le champ dont le nom est donné par l'utilisateur et retourne son index. La recherche tient compte de la casse. Si le champ n'est pas trouvé, elle renvoie -1.
		 * \param pchamp Nom du champ à rechercher
		 * \return Index du champ dans la structure
		 */
		int get_index(const string &pchamp) const;
	private:
		int _taille;	//!< Taille totale en octets d'un enregistrement
		int _nombre;	//!< Nombre total de champs dans la table
		int _position;	//!< Position d'insertion courante d'un nouveau champ, utilisée lors du remplissage de la structure
		Champ *champs;	//!< Tableau des champs de la table, respectivement dans l'ordre dans lequel ils sont lus. Cette variable indique la façon dont les données contenues devront être interprétées.
		int *positions;	//!< Dans les enregistrements, toutes les données sont stockées en un bloc, les unes à la suite des autres. Il n'est donc pas possible d'accéder directement à une donnée. Les valeurs contenues dans positions, qui est un tableau de même taille que types, indiquent la valeur du décalage mémoire à appliquer à partir du début de la structure de stockage pour obtenir la variable correspondante de l'enregistrement. Par définition, positions[0]=0.
		unsigned char* _index;	//!< Index permettant d'accélérer la recherche d'un champ à partir de son nom. L'index est créé lors de la première recherche par la fonction DescriptionTable::get_index(). Il s'agit d'un tableau de dimension _nombre, dans lequel chaque entrée correspond au numéro du champ du tableau champs dont le numéro d'ordre dans le classement alphabétique est l'indice de l'élément. Ex : le premier élément de ce tableau est la position du premier champs dans l'ordre alphabétique.

		/**
		 * \brief Crée l'index de recherche d'un champ
		 *
		 * La fonction crée l'index permettant d'accélérer les recherches de champs. Elle s'exécute en un temps O(n<sup>2</sup>) où n est le nombre de champs de la base, mais les recherches ultérieures ont alors une complexité O(log n) au lieu de O(n). Cette fonction est automatiquement appelée lors de l'ajout du dernier champ.
		 */
		void create_index();
};

/**
 * \class Enregistrement
 * \brief Définition d'un enregistrement de données.
 *
 * La classe définit un enregistrement de données, qui correspond à l'unité de données contenues dans une table. Chaque enregistrement représente un élément décrit dans la table.
 */
class Enregistrement {
	public:
		Enregistrement(const DescriptionTable *pdescription);	//!< Constructeur standard, à partir d'une structure décrivant la table. Le constructeur alloue la mémoire nécessaire pour les données de l'enregistrement

		/**
		 * \brief Constructeur standard
		 *
		 * Le constructeur n'initialise pas les variables.
		 */
		Enregistrement() {}

		/**
		 * \brief Constructeur de copie
		 *
		 * Le constructeur recopie l'enregistrement passé en argument ainsi que tous ses membres pour construire la nouvelle structure.
		 * \param e Enregistrement à copier
		 */
		Enregistrement(const Enregistrement &e);

		~Enregistrement();	//!< Destructeur standard, libère la mémoire utilisée par l'espace de stockage des données

		/**
		 * \brief Retourne la table de description des champs de l'enregistrement
		 *
		 * La fonction retourne la table de description des champs de l'enregistrement, sous la forme d'un pointeur vers une table constante afin de la protéger des modifications.
		 * \return Pointeur vers la table de description des champs de l'enregistrement
		 */
		const DescriptionTable *get_dt() {return description;}

		/**
		 * \brief Retourne l'élément spécifié en l'interprétant comme un entier
		 *
		 * La méthode retourne l'élément situé à la position index dans la structure de données data en l'interprétant comme un entier.
		 * \param index Index de l'élément à rechercher dans la structure data
		 * \return Entier situé à la position index dans la structure de données
		 */
		int get_int(int index) const throw (DataException) {return *((int*)(get_data(index)));}

		/**
		 * \brief Retourne l'élément spécifié en l'interprétant comme un entier
		 *
		 * La méthode retourne l'élément correspondant au champ dont le nom est pchamp dans la structure de données data en l'interprétant comme un entier.
		 * \param pchamp Nom de champ de l'élément à rechercher dans la structure data
		 * \return Entier correspondant au champ spécifié dans la structure de données
		 */
		int get_int(string pchamp) const throw (DataException) {return *((int*)(get_data(pchamp)));}

		/**
		 * \brief Retourne l'élément spécifié en l'interprétant comme un entier long
		 *
		 * La méthode retourne l'élément situé à la position index dans la structure de données data en l'interprétant comme un entier long.
		 * \param index Index de l'élément à rechercher dans la structure data
		 * \return Entier long situé à la position index dans la structure de données
		 */
		long get_long(int index) const throw (DataException) {return *((long*)(get_data(index)));}

		/**
		 * \brief Retourne l'élément spécifié en l'interprétant comme un entier long
		 *
		 * La méthode retourne l'élément correspondant au champ dont le nom est pchamp dans la structure de données data en l'interprétant comme un entier long.
		 * \param pchamp Nom de champ de l'élément à rechercher dans la structure data
		 * \return Entier long correspondant au champ spécifié dans la structure de données
		 */
		long get_long(string pchamp) const throw (DataException) {return *((long*)(get_data(pchamp)));}

		/**
		 * \brief Retourne l'élément spécifié en l'interprétant comme un flottant
		 *
		 * La méthode retourne l'élément situé à la position index dans la structure de données data en l'interprétant comme un flottant.
		 * \param index Index de l'élément à rechercher dans la structure data
		 * \return Flottant situé à la position index dans la structure de données
		 */
		float get_float(int index) const throw (DataException) {return *((float*)(get_data(index)));}

		/**
		 * \brief Retourne l'élément spécifié en l'interprétant comme un flottant
		 *
		 * La méthode retourne l'élément correspondant au champ dont le nom est pchamp dans la structure de données data en l'interprétant comme un flottant.
		 * \param pchamp Nom de champ de l'élément à rechercher dans la structure data
		 * \return Flottant correspondant au champ spécifié dans la structure de données
		 */
		float get_float(string pchamp) const throw (DataException) {return *((float*)(get_data(pchamp)));}

		/**
		 * \brief Retourne l'élément spécifié en l'interprétant comme un flottant double
		 *
		 * La méthode retourne l'élément situé à la position index dans la structure de données data en l'interprétant comme un flottant double.
		 * \param index Index de l'élément à rechercher dans la structure data
		 * \return Flottant double situé à la position index dans la structure de données
		 */
		double get_double(int index) const throw (DataException) {return *((double*)(get_data(index)));}

		/**
		 * \brief Retourne l'élément spécifié en l'interprétant comme un flottant double
		 *
		 * La méthode retourne l'élément correspondant au champ dont le nom est pchamp dans la structure de données data en l'interprétant comme un flottant double.
		 * \param pchamp Nom de champ de l'élément à rechercher dans la structure data
		 * \return Flottant double correspondant au champ spécifié dans la structure de données
		 */
		double get_double(string pchamp) const throw (DataException) {return *((double*)(get_data(pchamp)));}

		/**
		 * \brief Retourne l'élément spécifié en l'interprétant comme une chaîne de caractères
		 *
		 * La méthode retourne l'élément situé à la position index dans la structure de données data en l'interprétant comme une chaîne de caractères.
		 * \param index Index de l'élément à rechercher dans la structure data
		 * \return Chaîne de caractères située à la position index dans la structure de données
		 */
		string get_string(int index) const throw (DataException) {return **((string**)(get_data(index)));}

		/**
		 * \brief Retourne l'élément spécifié en l'interprétant comme une chaîne de caractères
		 *
		 * La méthode retourne l'élément correspondant au champ dont le nom est pchamp dans la structure de données data en l'interprétant comme une chaîne de caractères.
		 * \param pchamp Nom de champ de l'élément à rechercher dans la structure data
		 * \return Chaîne de caractères correspondant au champ spécifié dans la structure de données
		 */
		string get_string(string pchamp) const throw (DataException) {return **((string**)(get_data(pchamp)));}

		//@{
		/**
		 * \brief Définit l'élément spécifié dans la structure de données
		 *
		 * La méthode définit l'élément spécifié par son index dans la structure de données data en lui donnant la valeur mentionnée par le paramètre valeur.
		 * Si le type de la valeur ne correspond pas au type attendu, une exception du type BAD_FORMAT est soulevée.
		 * \param index Numéro de l'élément dont la valeur sera définie
		 * \param valeur Nouvelle valeur du champ
		 */
		void set(int index,int valeur) throw (DataException);
		void set(int index,long valeur) throw (DataException);
		void set(int index,float valeur) throw (DataException);
		void set(int index,double valeur) throw (DataException);
		void set(int index,const string &valeur) throw (DataException);
		//@}

		/**
		 * \brief Affiche l'élément spécifié par le paramètre index dans le flux de données
		 *
		 * La fonction affiche l'élément dont l'index est donné en paramètre dans le flux de données mentionné.
		 * \param out Flux de données sur lequel afficher l'élément
		 * \param index Numéro de l'élément à afficher
		 */
		void display(ostream &out,int index) throw (DataException);

		/**
		 * \brief Affiche l'élément spécifié par le paramètre pchamp dans le flux de données
		 *
		 * La fonction affiche l'élément de l'enregistrement dont le nom est pchamp dans le flux de données mentionné.
		 * \param out Flux de données sur lequel afficher l'élément
		 * \param pchamp Nom du champ de l'élément à afficher
		 */
		void display(ostream &out,string pchamp) throw (DataException) {display(out,description->get_index(pchamp));}
	protected:
		const DescriptionTable *description;	//!< Description de la structure de la table de données, qui indique comment interpréter les valeurs contenues dans l'enregistrement
		/**
		 * \brief Données contenues dans l'enregistrement
		 *
		 * data représente les données de l'enregistrement, stockées sous forme binaire et de manière compacte pour éviter toute déperdition de mémoire. Toutes les données sont stockées les unes à la suite des autres à l'emplacement mémoire indiqué par la variable data. Pour les données de type entier ou flottant, leur valeur est directement stockée dans la mémoire. Les données de type chaîne dont la taille en mémoire ne peut pas être connue à l'avance sont représentées par un simple pointeur qui indique l'emplacement mémoire plus large où elles sont stockées en entier.
		 */
		char *data;

		/**
		 * \brief Retourne un pointeur sur l'élément recherché
		 *
		 * La méthode retourne un pointeur sur le début de l'élément situé à la position index dans la structure de données data. Si l'élément n'est pas trouvé, une exception BAD_REFERENCE est soulevée.
		 * \param index Index de l'élément à rechercher dans la structure data
		 * \return Pointeur de type char* sur le début de l'élément recherché
		 */
		char *get_data(int index) const throw (DataException);

		/**
		 * \brief Retourne un pointeur sur l'élément recherché
		 *
		 * La méthode retourne un pointeur sur le début de l'élément dont le nom de champ est pchamp dans la structure de données data. Si l'élément n'est pas trouvé, une exception BAD_REFERENCE est soulevée.
		 * \param pchamp Nom du champ à rechercher dans la structure de données
		 * \return Pointeur de type char* sur le début de l'élément recherché
		 */
		char *get_data(const string &pchamp) const throw (DataException);
};

#endif   // ----- #ifndef ATTRIBUTS_H_INC  -----
